import { cloneVNode, App, h, VNode, VNodeChild, Plugin } from 'vue'
import { isObject } from '@vue/shared'
import { render } from '../_util/props-util'
import { MessageProps } from '../MessageManager/Message'
import { newInstace } from '../MessageManager'

import Icon from '../icon'
import './style'

const iconMap = {
  success: h(Icon, { type: 'check-circle' }),
  warning: h(Icon, { type: 'warn-circle' }),
  error: h(Icon, { type: 'close-circle' }),
  info: h(Icon, { type: 'info-circle' }),
  loading: h(Icon, { type: 'loading', spin: true })
}

type MessageType = keyof typeof iconMap

type ContentType = VNodeChild | (() => VNodeChild)

export type MessageOption = MessageProps & {
  content?: ContentType
  icon?: VNode
  duration?: number
  type?: MessageType
  closable?: boolean
}

const prefixCls = 'x-message'
const instance = newInstace({ prefixCls })

let __key = 0
function getKey() {
  return '__' + __key++
}

interface MessageFn {
  (opt: MessageOption): void
  (content: string, options: MessageOption): void
}

export interface MessageApi extends MessageFn, Record<MessageType, MessageFn> {
  destroyAll(): void
  install: Plugin['install']
}

const Message: Partial<MessageApi> = (content: MessageOption | string, options?: MessageOption) => {
  options = typeof content == 'string' ? { ...options, content } : content
  const key = options.key ?? getKey()
  const obj = instance.add({
    ...options,
    key,
    default: () => (
      <div class={[prefixCls, `${prefixCls}-${options.type}`]}>
        {cloneVNode(options.icon || iconMap[options.type ?? 'info'], { class: `${prefixCls}_icon` })}
        {render(options.content)}
        {options.closable && <Icon type='cross' class={`${prefixCls}_close`} onClick={onClick} />}
      </div>
    )
  })

  function onClick(e: MouseEvent) {
    e.preventDefault()
    e.stopPropagation()
    obj.close()
  }
  return obj.close
}

Message.destroyAll = () => instance.removeAll()

const messageTypes = Object.keys(iconMap) as MessageType[]

messageTypes.forEach(type => {
  Message[type] = (arg1: MessageOption | string, options?: MessageOption) => {
    const content = isObject(arg1) ? arg1.content : arg1
    // @ts-ignore
    return Message({ content, ...options, type })
  }
})

Message.install = (app: App) => {
  app.config.globalProperties.$message = Message as MessageApi
}

export default Message as MessageApi
